package com.bocek.chakmapoint.controller;

import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;
import java.util.Iterator;

import javax.swing.JOptionPane;
import javax.swing.JPanel;

import com.bocek.chakmapoint.model.CText;
import com.bocek.chakmapoint.model.CZigZagLine;
import com.bocek.chakmapoint.model.COval;
import com.bocek.chakmapoint.model.CRectangle;
import com.bocek.chakmapoint.model.Slide;
import com.bocek.chakmapoint.model.SlideItem;
import com.bocek.chakmapoint.model.SlideList;
import com.bocek.chakmapoint.model.SlideStyleList;

import com.bocek.chakmapoint.view.slide.SlideView;
import com.bocek.chakmapoint.view.toolbar.ToolbarView;

public class SlideEditController extends KeyAdapter implements MouseListener,
		MouseMotionListener {

	private static SlideEditController instance = null;

	private JPanel p;

	private int currentSlideNo;
	private Slide currentSlide;

	private String currentState = "idle";
	private boolean draggingSelect = false;

	private SlideItem selectedItem = null;
	private ArrayList<SlideItem> selectedItems = null;

	private String previousState = null;
	private Point previousPoint = null;

	private boolean showNumber = false;

	private SlideEditController() {
		currentState = "idle";
	}

	public static SlideEditController getInstance() {
		if (instance == null) {
			instance = new SlideEditController();
		}

		return instance;
	}

	public void setDrawPanel(JPanel panel) {
		p = panel;
	}

	public void activate() {
		if(currentSlideNo > SlideList.getSlides().size())
		{		
			currentSlideNo = 1;
			currentSlide = SlideList.getSlide(currentSlideNo);
		}
		
		currentState = "idle";
		currentSlide.setCurrentCreation(null);
		selectedItem = null;
		selectedItems = null;
		previousState = null;
		previousPoint = null;
		draggingSelect = false;
		
		drawSlide();
	}

	public Color getSelectedItemColor() {
		if (selectedItem == null) {
			return SlideStyleList.getInstance().getStyle(0).getColor();
		} else {
			return selectedItem.getColor();
		}
	}

	public int getSelectedItemThickness() {
		if (selectedItem == null) {
			return SlideStyleList.getInstance().getStyle(0).getThickness();
		} else {
			return selectedItem.getThickness();
		}
	}

	public void addSlideItem(SlideItem slideItem) {
		currentSlide.addSlideItem(slideItem);
	}

	public void removeSlideItem(SlideItem slideItem) {
		currentSlide.removeSlideItem(slideItem);
	}

	public String getCurrentSlideName() {
		return currentSlide.getName();
	}

	public boolean isNumberShown() {
		return showNumber;
	}

	public int getCurrentSlideNo() {
		return currentSlideNo;
	}

	public int getSlideCount() {
		return SlideList.getSlides().size();
	}

	public void nextSlide() {
		if (currentSlide != null) {
			if (currentSlideNo == getSlideCount()) {
				goToSlide(1);
			} else {
				currentSlide = SlideList.getSlide(++currentSlideNo);
				activate();
			}
		}
	}

	public void previousSlide() {
		if (currentSlide != null) {
			if (currentSlideNo == 1) {
				goToSlide(getSlideCount());
			} else {
				currentSlide = SlideList.getSlide(--currentSlideNo);
				activate();
			}
		}
	}

	public void goToSlide(int newSlideNo) {
		currentSlideNo = newSlideNo;
		currentSlide = SlideList.getSlide(newSlideNo);
		activate();
	}

	public SlideItem getCurrentCreation() {
		if (currentSlide != null)
			return currentSlide.getCurrentCreation();

		else
			return null;
	}

	public Iterator<SlideItem> getItemIterator() {
		if (currentSlide != null) {
			return currentSlide.getReverseSlideItems().iterator();
		} else {
			return null;
		}
	}

	public void drawSlide() {
		if (p != null)
			p.repaint();
	}

	public void confirmRemove() {
	}

	/*
	 * "idle" = Idle state, ready to select "selected" = An item is selected
	 * "multipleSelected" = More than one item is selected "dragging" = Dragging
	 * an item "multipleDragging" = Dragging more than one item
	 * "rectangleCreation" = Rectangle is ready to be created
	 * "rectangleCreation2" = Rectangle's first point is clicked, ready for the
	 * 2nd point. "ovalCreation" & "ovalCreation2" = Same as above
	 * "zigzagCreation" = Same as above "zigzagCreation2" = First point is
	 * created, this is the state until its done. "textCreation" = Text button
	 * is pressed, ready to denote a position.
	 */
	public String getCurrentState() {
		return currentState;
	}

	public String getSelectedItemType() {
		if (selectedItem != null) {
			return selectedItem.getName();
		} else {
			return null;
		}
	}

	public void toolbarIdle() {
		((CardLayout) ToolbarView.getInstance().getLayout()).show(
				ToolbarView.getInstance(), "selectView");
	}

	public void handleMouseAction(MouseEvent event) {
		if (currentSlide != null) {
			if (currentState.equals("idle")) {
				handleIdleMouseAction(event);
			}

			else if (currentState.equals("selected")) {
				handleSelectedMouseAction(event);
			}

			else if (currentState.equals("multipleSelected")) {
				handleMultipleSelectedMouseAction(event);
			}

			else if (currentState.equals("zigzagCreation")
					|| currentState.equals("zigzagCreation2")) {
				handleZigZagCreation(event);
			}

			else if (currentState.equals("rectangleCreation")
					|| currentState.equals("rectangleCreation2")) {
				handleRectangleCreation(event);
			}

			else if (currentState.equals("ovalCreation")
					|| currentState.equals("ovalCreation2")) {
				handleOvalCreation(event);
			}

			else if (currentState.equals("textCreation")) {
				handleTextCreation(event);
			}
			
			drawSlide();
		}
	}

	private void handleRightMouseAction(MouseEvent arg0) {
		if (currentSlide != null) {
			if (currentState.equals("zigzagCreation2")) {
				endZigZagCreation();
			}
		}
	}

	private void handleIdleMouseAction(MouseEvent event) {
		ArrayList<SlideItem> currentSlideItems = currentSlide.getSlideItems();
		Point point = event.getPoint();

		if (currentSlide != null) {
			for (SlideItem si : currentSlideItems) {
				if (si.select(point)) {

					((CardLayout) ToolbarView.getInstance().getLayout()).show(
							ToolbarView.getInstance(), si.getName()
									.toLowerCase() + "View");

					selectedItem = si;
					currentState = "selected";
					break;
				}
			}
		}
	}

	private void handleSelectedMouseAction(MouseEvent event) {
		Point point = event.getPoint();

		if (!selectedItem.isPointInside(point)) {
			selectedItem.deselect();
			selectedItem = null;
			currentState = "idle";

			toolbarIdle();
		}
	}

	private void handleMultipleSelectedMouseAction(MouseEvent event) {
		Point point = event.getPoint();

		boolean hit = false;

		for (SlideItem si : selectedItems) {
			if (si.isPointInside(point)) {
				hit = true;
				break;
			}
		}

		if (!hit) {
			for (SlideItem si : selectedItems) {
				si.deselect();
			}

			selectedItems = null;
			currentState = "idle";
		}
	}

	private void handleZigZagCreation(MouseEvent event) {
		Point point = event.getPoint();

		if (currentState.equals("zigzagCreation")) {
			currentSlide.setCurrentCreation(new CZigZagLine(point));
			currentState = "zigzagCreation2";
		} else if (currentState.equals("zigzagCreation2")) {
			((CZigZagLine) currentSlide.getCurrentCreation()).addPoint(point);
			((CZigZagLine) currentSlide.getCurrentCreation()).resetDrawDrag();
		}
	}

	public void endZigZagCreation() {
		currentSlide.finalizeCurrentCreation();
		drawSlide();

		currentState = "idle";

		toolbarIdle();
	}

	private void handleRectangleCreation(MouseEvent event) {
		Point point = event.getPoint();

		if (currentState.equals("rectangleCreation")) {
			currentSlide.setCurrentCreation(new CRectangle(point));

			currentState = "rectangleCreation2";
		} else if (currentState.equals("rectangleCreation2")) {
			((CRectangle) currentSlide.getCurrentCreation()).addPoint(point);
			currentSlide.finalizeCurrentCreation();
			drawSlide();

			currentState = "idle";

			toolbarIdle();
		}
	}

	private void handleOvalCreation(MouseEvent event) {
		Point point = event.getPoint();

		if (currentState.equals("ovalCreation")) {
			currentSlide.setCurrentCreation(new COval(point));

			currentState = "ovalCreation2";
		} else if (currentState.equals("ovalCreation2")) {
			((COval) currentSlide.getCurrentCreation()).addPoint(point);
			currentSlide.finalizeCurrentCreation();
			drawSlide();

			currentState = "idle";

			toolbarIdle();
		}
	}

	private void handleTextCreation(MouseEvent event) {
		Point point = event.getPoint();

		String value = JOptionPane.showInputDialog(null, "Enter your input: ",
				"Create new Text", 1);

		CText text = new CText();
		text.setValue(value);
		text.move(point);

		currentSlide.addSlideItem(text);

		drawSlide();

		currentState = "idle";

		toolbarIdle();
	}

	private void handleMouseDrag(MouseEvent event) {
		if (currentSlide != null) {
			Point point = event.getPoint();

			if (currentState.equals("dragging")) {
				int pointX = (int) (point.getX() - previousPoint.getX());
				int pointY = (int) (point.getY() - previousPoint.getY());

				Point delta = new Point(pointX, pointY);

				selectedItem.move(delta);
				drawSlide();

				previousPoint = point;
			} else if (draggingSelect) {

			} else if (currentState.equals("multipleDragging")) {
				int pointX = (int) (point.getX() - previousPoint.getX());
				int pointY = (int) (point.getY() - previousPoint.getY());

				Point delta = new Point(pointX, pointY);

				for (SlideItem si : selectedItems) {
					si.move(delta);
				}

				drawSlide();

				previousPoint = point;
			}
		}
	}

	public void handleMouseMove(MouseEvent event) {
		if (currentSlide != null) {
			Point point = event.getPoint();

			if (currentState.equals("rectangleCreation2")
					|| currentState.equals("ovalCreation2")
					|| currentState.equals("zigzagCreation2")) {

				if (currentSlide.getCurrentCreation().getName()
						.equals("Rectangle")) {
					((CRectangle) currentSlide.getCurrentCreation())
							.addPoint(point);
				} else if (currentSlide.getCurrentCreation().getName()
						.equals("Oval")) {
					((COval) currentSlide.getCurrentCreation()).addPoint(point);
				} else if (currentSlide.getCurrentCreation().getName()
						.equals("ZigZagLine")) {
					((CZigZagLine) currentSlide.getCurrentCreation())
							.drawDrag(point);
				}

				drawSlide();
			}
		}
	}

	private void handleMousePress(MouseEvent event) {
		if (currentSlide != null) {
			Point point = event.getPoint();

			if (currentState.equals("selected")) {
				if (selectedItem.isPointInside(point)) {
					previousPoint = point;
					previousState = "selected";
					currentState = "dragging";
				}
			}

			else if (currentState.equals("multipleSelected")) {

				for (SlideItem si : selectedItems) {
					if (si.isPointInside(point)) {
						previousPoint = point;
						previousState = "multipleSelected";
						currentState = "multipleDragging";
						break;
					}
				}
			}

			else if (currentState.equals("idle")) {
				draggingSelect = true;
				previousPoint = point;
			}
		}
	}

	private void handleMouseRelease(MouseEvent event) {
		if (currentSlide != null) {
			Point point = event.getPoint();

			if (draggingSelect) {
				selectArea(this.previousPoint, point);

				draggingSelect = false;
				previousPoint = null;
			}

			else if (previousPoint != null) {
				previousPoint = null;
				if (previousState != null) {
					currentState = previousState;
					previousState = null;
				}
			}
		}
	}

	private void selectArea(Point startPoint, Point endPoint) {
		this.selectedItems = new ArrayList<SlideItem>();
		ArrayList<SlideItem> currentItems = currentSlide.getSlideItems();

		int firstX = (int) startPoint.getX();
		int firstY = (int) startPoint.getY();

		int secondX = (int) endPoint.getX();
		int secondY = (int) endPoint.getY();

		int width = Math.abs(firstX - secondX);
		int height = Math.abs(firstY - secondY);

		Rectangle selectBox = null;

		if ((firstX > secondX) && (firstY <= secondY)) {
			selectBox = new Rectangle(secondX, firstY, width, height);
		} else if ((firstX <= secondX) && (firstY > secondY)) {
			selectBox = new Rectangle(firstX, secondY, width, height);
		} else if ((firstX > secondX) && (firstY > secondY)) {
			selectBox = new Rectangle(secondX, secondY, width, height);
		} else {
			selectBox = new Rectangle(firstX, firstY, width, height);
		}

		for (SlideItem si : currentItems) {
			if (si.intersects(selectBox)) {
				this.selectedItems.add(si);
				si.setSelected(true);
			}
		}

		switch (selectedItems.size()) {
		case 0:
			selectedItems = null;
			break;
		case 1:
			selectedItem = selectedItems.get(0);
			selectedItems = null;
			currentState = "selected";
			break;
		default:
			currentState = "multipleSelected";
			break;
		}

		drawSlide();
	}

	public void createNewSlideButtonPressed() {
		String value = JOptionPane.showInputDialog(null, "Enter Slide name: ",
				"Create new Slide", 1);

		Slide newSlide = null;

		if (value.equals("")) {
			newSlide = new Slide();
		} else {
			newSlide = new Slide(value);
		}

		SlideList.appendSlide(newSlide);

		currentSlide = newSlide;
		currentSlideNo = getSlideCount();

		drawSlide();
	}

	public void moveToFrontButtonPressed() {
		if (currentSlide != null) {
			currentSlide.moveItemToFront(selectedItem);
			drawSlide();
		}
	}

	public void moveToBackButtonPressed() {
		if (currentSlide != null) {
			currentSlide.moveItemToBack(selectedItem);
			drawSlide();
		}
	}

	public void deleteButtonPressed() {
		currentSlide.removeSlideItem(selectedItem);
		drawSlide();
	}

	public void toggleFillButtonPressed() {
		selectedItem.fill();
		drawSlide();
	}

	public void setThickness(int thickness) {
		if (selectedItem != null) {
			if (selectedItem.getName().equals("Rectangle")) {
				((CRectangle) selectedItem).setThickness(thickness);
				drawSlide();
			}

			else if (selectedItem.getName().equals("ZigZagLine")) {
				((CZigZagLine) selectedItem).setThickness(thickness);
				drawSlide();
			}

			else if (selectedItem.getName().equals("Oval")) {
				((COval) selectedItem).setThickness(thickness);
				drawSlide();
			}
		}
	}

	public void setColor(Color color) {
		selectedItem.setColor(color);

		drawSlide();
	}

	public void toggleBold() {
		int fontStyle = ((CText) selectedItem).getFontStyle();

		if (fontStyle == Font.ITALIC) {
			((CText) selectedItem).changeFontStyle(Font.ITALIC | Font.BOLD);
		} else if (fontStyle == Font.BOLD) {
			((CText) selectedItem).changeFontStyle(Font.PLAIN);
		} else if (fontStyle == (Font.BOLD | Font.ITALIC)) {
			((CText) selectedItem).changeFontStyle(Font.ITALIC);
		} else if (fontStyle == Font.PLAIN) {
			((CText) selectedItem).changeFontStyle(Font.BOLD);
		}

		drawSlide();
	}

	public void toggleItalic() {
		int fontStyle = ((CText) selectedItem).getFontStyle();

		if (fontStyle == Font.ITALIC) {
			((CText) selectedItem).changeFontStyle(Font.PLAIN);
		} else if (fontStyle == Font.BOLD) {
			((CText) selectedItem).changeFontStyle(Font.BOLD | Font.ITALIC);
		} else if (fontStyle == (Font.BOLD | Font.ITALIC)) {
			((CText) selectedItem).changeFontStyle(Font.BOLD);
		} else if (fontStyle == Font.PLAIN) {
			((CText) selectedItem).changeFontStyle(Font.ITALIC);
		}

		drawSlide();
	}

	public void toggleShowNumber() {
		showNumber = !showNumber;
		drawSlide();
	}

	public void deleteSelected() {
		if (selectedItem != null)
			removeSlideItem(selectedItem);
	}

	public void createRectangleButtonPressed() {
		currentState = "rectangleCreation";
	}

	public void createTextButtonPressed() {
		currentState = "textCreation";
	}

	public void createOvalButtonPressed() {
		currentState = "ovalCreation";
	}

	public void createZigZagButtonPressed() {
		currentState = "zigzagCreation";
	}

	public void cancelButtonPressed() {
		currentState = "idle";
	}

	public void addTitle() {
		if (currentSlide != null) {
			CText title = new CText();
			title.setValue(currentSlide.getName());
			title.move(new Point(10, 30));

			title.changeFontSize(24);
			title.changeFontStyle(Font.ITALIC | Font.BOLD);

			currentSlide.addSlideItem(title);

			drawSlide();
		}
	}

	public void handleKeyboardAction() {
	}

	@Override
	public void mouseDragged(MouseEvent arg0) {
		handleMouseDrag(arg0);
	}

	@Override
	public void mouseMoved(MouseEvent arg0) {
		handleMouseMove(arg0);
	}

	@Override
	public void mouseClicked(MouseEvent arg0) {
		if (arg0.isMetaDown()) {
			handleRightMouseAction(arg0);
		} else {
			handleMouseAction(arg0);
		}
	}

	@Override
	public void mouseEntered(MouseEvent arg0) {
	}

	@Override
	public void mouseExited(MouseEvent arg0) {
	}

	@Override
	public void mousePressed(MouseEvent arg0) {
		handleMousePress(arg0);
	}

	@Override
	public void mouseReleased(MouseEvent arg0) {
		handleMouseRelease(arg0);
	}

	@Override
	public void keyPressed(KeyEvent evt) {
		if (evt.getKeyChar() == KeyEvent.VK_BACK_SPACE) {
			if (currentState.equals("selected")) {
				removeSlideItem(selectedItem);

				currentState = "idle";
				toolbarIdle();
			}
		}
	}
}
